
#include <BWidget>

#include <BStackLayout>
#include "member_BStackLayout.h"
#include "member_BWidget.h"
#include "member_BLayout.h"
#include "member_BSpacer.h"
#include "member_BSpring.h"

using namespace BWE;

#define member					(*(member_BStackLayout*)_ptr)
#define member_allocate()		_ptr = new member_BStackLayout(this)
#define member_release()		delete (member_BStackLayout*)_ptr

BStackLayout::BStackLayout(BWidget* host) : BLayout(host)
{
	member_allocate();
}
BStackLayout::BStackLayout(const BString& name, BWidget* host) : BLayout(name, host)
{
	member_allocate();
}
BStackLayout::BStackLayout(Policy sizePolicy, BWidget* host) : BLayout(host)
{
	member_allocate();
	this->setSizePolicy(BSizePolicy(sizePolicy));
}
BStackLayout::BStackLayout(Policy widthPolicy, Policy heightPolicy, BWidget* host) : BLayout(host)
{
	member_allocate();
	this->setSizePolicy(BSizePolicy(widthPolicy, heightPolicy));
}
BStackLayout::~BStackLayout()
{
	clear();
	member_release();
}

bool BStackLayout::setAlign(int pos, Align align)
{
	if (member.items.check(pos))
		return false;
	ILayoutItem& item = member.items[pos];
	if (item.align != align)
	{
		item.align = align;
		BLayout::dirty();
	}
	return true;
}
Align BStackLayout::align(int pos) const
{
	return member.items(pos).align;
}

bool BStackLayout::setAlign(BObject* object, Align align)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item != object)
			continue;
		if (item.align != align)
		{
			item.align = align;
			BLayout::dirty();
		}
		return true;
	}
	return false;
}
Align BStackLayout::align(BObject* object) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item != object)
			continue;
		return item.align;
	}
	return Align_None;
}

bool BStackLayout::setCurrentIndex(int current)
{
	if (!member.items.check(current))
		current = -1;
	if (member.current != current)
	{
		if (member.current > -1)
		{
			ILayoutItem& item = member.items[member.current];
			if (item.widget)
				item.widget->setVisible(false);
			if (item.layout)
				item.layout->setVisible(false);
		}
		member.current = current;
		if (member.current > -1)
		{
			ILayoutItem& item = member.items[member.current];
			if (item.widget)
				item.widget->setVisible(true);
			if (item.layout)
				item.layout->setVisible(true);
		}
		BLayout::dirty();
		emit(Signal_CurrentChanged, member.current);
		return true;
	}
	return false;
}
int BStackLayout::currentIndex() const
{
	return member.current;
}

bool BStackLayout::setCurrent(BObject* object)
{
	if (!object)
		return setCurrentIndex(-1);
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget == object)
			return setCurrentIndex(i);
		if (item.spring == object)
			return setCurrentIndex(i);
		if (item.spacer == object)
			return setCurrentIndex(i);
		if (item.layout == object)
			return setCurrentIndex(i);
	}
	return false;
}
BObject* BStackLayout::current() const
{
	if (member.items.check(member.current))
	{
		ILayoutItem& item = member.items(member.current);
		if (item.widget)
			return item.widget;
		else if (item.layout)
			return item.layout;
		else if (item.spacer)
			return item.spacer;
		else if (item.spring)
			return item.spring;
	}
	return 0;
}

bool BStackLayout::addWidget(BWidget* widget, Align align)
{
	if (!widget)
		return false;

	if (this->host() == widget)
		return false;

	if (widget_member(widget)->upperLayout == this)
		return true;

	ILayoutItem& item = member.items.append();
	item.widget = widget;
	item.align = align;
	if (widget_member(widget)->upperLayout)
		widget_member(widget)->upperLayout->remove(widget);
	widget_member(widget)->upperLayout = this;
	if (layout_member(this)->host)
		layout_member(this)->host->addChild(widget);
	member.connect(widget, Signal_Show, &member_BStackLayout::slotWidgetShown);

	if (member.items.size() == 1)
		setCurrentIndex(0);
	else
		widget->setVisible(false);

	BLayout::dirty();
	return true;
}
bool BStackLayout::addLayout(BLayout* layout, Align align)
{
	if (!layout || layout == this)
		return false;

	ILayoutItem& item = member.items.append();
	item.layout = layout;
	item.align = align;

	if (layout_member(layout)->parent)
		layout_member(layout)->parent->remove(layout);
	layout_member(layout)->parent = this;
	layout_member(layout)->setHost(layout_member(this)->host);

	if (member.items.size() == 1)
		setCurrentIndex(0);
	else
		layout->setVisible(false);

	BLayout::dirty();
	return true;
}
bool BStackLayout::addSpacer(BSpacer* spacer, Align align)
{
	if (!spacer)
		return false;

	if (spacer_member(spacer)->layout == this)
		return true;

	ILayoutItem& item = member.items.append();
	item.spacer = spacer;
	item.align = align;

	if (spacer_member(spacer)->layout)
		spacer_member(spacer)->layout->remove(spacer);
	spacer_member(spacer)->layout = this;

	if (member.items.size() == 1)
		setCurrentIndex(0);

	BLayout::dirty();
	return true;
}
BSpacer* BStackLayout::addSpacer(int width, int height)
{
	BSpacer* spacer = new BSpacer(width, height);
	addSpacer(spacer);
	return spacer;
}
BSpacer* BStackLayout::addSpacer(const BSize& size)
{
	BSpacer* spacer = new BSpacer(size);
	addSpacer(spacer);
	return spacer;
}

bool BStackLayout::insertWidget(int pos, BWidget* widget, Align align)
{
	if (!widget)
		return false;

	if (pos < 0 || pos > member.items.size())
		return false;

	int index = member.items.find(widget);
	if (index < member.items.size())
	{
		member.items.move(index, pos);
		if (member.current >= pos)
			setCurrentIndex(member.current + 1);
	}
	else
	{
		ILayoutItem& item = member.items.insert(pos);
		item.widget = widget;
		item.align = align;
		if (widget_member(widget)->upperLayout)
			widget_member(widget)->upperLayout->remove(widget);
		widget_member(widget)->upperLayout = this;
		if (layout_member(this)->host)
			layout_member(this)->host->addChild(widget);
		if (member.items.size() == 1)
			setCurrentIndex(0);
	}

	BLayout::dirty();
	return true;
}
bool BStackLayout::insertLayout(int pos, BLayout* layout, Align align)
{
	if (!layout)
		return false;

	if (pos < 0 || pos > member.items.size())
		return false;

	int index = member.items.find(layout);
	if (index < member.items.size())
	{
		member.items.move(index, pos);
		if (member.current >= pos)
			setCurrentIndex(member.current + 1);
	}
	else
	{
		ILayoutItem& item = member.items.insert(pos);
		item.layout = layout;
		item.align = align;
		if (layout_member(layout)->parent)
			layout_member(layout)->parent->remove(layout);
		layout_member(layout)->parent = this;
		layout_member(layout)->setHost(layout_member(this)->host);
		if (member.items.size() == 1)
			setCurrentIndex(0);
	}

	BLayout::dirty();
	return true;
}
bool BStackLayout::insertSpacer(int pos, BSpacer* spacer, Align align)
{
	if (!spacer)
		return false;

	if (pos < 0 || pos > member.items.size())
		return false;

	int index = member.items.find(spacer);
	if (index < member.items.size())
	{
		member.items.move(index, pos);
		if (member.current >= pos)
			setCurrentIndex(member.current + 1);
	}
	else
	{
		ILayoutItem& item = member.items.insert(pos);
		item.spacer = spacer;
		item.align = align;
		if (spacer_member(spacer)->layout)
			spacer_member(spacer)->layout->remove(spacer);
		spacer_member(spacer)->layout = this;
		if (member.items.size() == 1)
			setCurrentIndex(0);
	}

	BLayout::dirty();
	return true;
}
BSpacer* BStackLayout::insertSpacer(int pos, int width, int height)
{
	BSpacer* spacer = new BSpacer(width, height);
	insertSpacer(pos, spacer);
	return spacer;
}
BSpacer* BStackLayout::insertSpacer(int pos, const BSize& size)
{
	BSpacer* spacer = new BSpacer(size);
	insertSpacer(pos, spacer);
	return spacer;
}

bool BStackLayout::remove(int pos)
{
	ILayoutItem& item = member.items(pos);
	if (item.widget)
		return remove(item.widget);
	else if (item.layout)
		return remove(item.layout);
	else if (item.spacer)
		return remove(item.spacer);
	else if (item.spring)
		return remove(item.spring);
	return false;
}
int BStackLayout::index(const BObject* object) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget == object)
			return i;
		if (item.spring == object)
			return i;
		if (item.spacer == object)
			return i;
		if (item.layout == object)
			return i;
	}
	return -1;
}
int BStackLayout::index(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget && item.widget->name() == name)
			return i;
		if (item.spring && item.spring->name() == name)
			return i;
		if (item.spacer && item.spacer->name() == name)
			return i;
		if (item.layout && item.layout->name() == name)
			return i;
	}
	return -1;
}
BRect BStackLayout::cellRect(int pos) const
{
	return member.items(pos).rect;
}

int BStackLayout::count() const
{
	return member.items.size();
}
BObject* BStackLayout::object(int pos) const
{
	if (member.items(pos).widget)
		return member.items(pos).widget;
	else if (member.items(pos).layout)
		return member.items(pos).layout;
	else if (member.items(pos).spacer)
		return member.items(pos).spacer;
	else if (member.items(pos).spring)
		return member.items(pos).spring;
	return 0;
}
BWidget* BStackLayout::widget(int index) const
{
	return member.items(index).widget;
}
BLayout* BStackLayout::layout(int index) const
{
	return member.items(index).layout;
}
BSpacer* BStackLayout::spacer(int index) const
{
	return member.items(index).spacer;
}
BSpring* BStackLayout::spring(int index) const
{
	return member.items(index).spring;
}

bool BStackLayout::dirty(const BObject* object)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		if (member.items[i] == object)
		{
			member.items[i].dirty = true;
			BLayout::dirty();
			return true;
		}
	}
	return BLayout::dirty(object);
}
Align BStackLayout::align(const BObject* object) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget == object)
			return item.align;
		if (item.layout == object)
			return item.align;
		if (item.spacer == object)
			return item.align;
		if (item.spring == object)
			return item.align;
	}
	return BLayout::align(object);
}
const BRect& BStackLayout::area(const BObject* object) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget == object)
			return item.rect;
		if (item.layout == object)
			return item.rect;
		if (item.spacer == object)
			return item.rect;
		if (item.spring == object)
			return item.rect;
	}
	return BLayout::area(object);
}

BObject* BStackLayout::find(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget && item.widget->name() == name)
			return item.widget;
		if (item.spring && item.spring->name() == name)
			return item.spring;
		if (item.spacer && item.spacer->name() == name)
			return item.spacer;
		if (item.layout)
		{
			if (item.layout->name() == name)
				return item.layout;
			BObject* object = item.layout->find(name);
			if (object)
				return object;
		}
	}
	return 0;
}
BWidget* BStackLayout::findWidget(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.widget && item.widget->name() == name)
			return item.widget;
		if (item.layout)
		{
			BWidget* widget = item.layout->findWidget(name);
			if (widget)
				return widget;
		}
	}
	return 0;
}
BLayout* BStackLayout::findLayout(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.layout)
		{
			if (item.layout->name() == name)
				return item.layout;
			BLayout* layout = item.layout->findLayout(name);
			if (layout)
				return layout;
		}
	}
	return 0;
}
BSpacer* BStackLayout::findSpacer(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.spacer && item.spacer->name() == name)
			return item.spacer;
		if (item.layout)
		{
			BSpacer* spacer = item.layout->findSpacer(name);
			if (spacer)
				return spacer;
		}
	}
	return 0;
}
BSpring* BStackLayout::findSpring(const BString& name) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.spring && item.spring->name() == name)
			return item.spring;
		if (item.layout)
		{
			BSpring* spring = item.layout->findSpring(name);
			if (spring)
				return spring;
		}
	}
	return 0;
}

bool BStackLayout::contain(const BWidget* widget) const
{
	if (!widget)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		const ILayoutItem& item = member.items[i];
		if (item.widget == widget)
			return true;
		if (item.layout && item.layout->contain(widget))
			return true;
	}
	return false;
}
bool BStackLayout::contain(const BLayout* layout) const
{
	if (!layout)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		const ILayoutItem& item = member.items[i];
		if (item.layout == layout)
			return true;
		if (item.layout && item.layout->contain(layout))
			return true;
	}
	return false;
}
bool BStackLayout::contain(const BSpacer* spacer) const
{
	if (!spacer)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		const ILayoutItem& item = member.items[i];
		if (item.spacer == spacer)
			return true;
		if (item.layout && item.layout->contain(spacer))
			return true;
	}
	return false;
}
bool BStackLayout::contain(const BSpring* spring) const
{
	if (!spring)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		const ILayoutItem& item = member.items[i];
		if (item.spring == spring)
			return true;
		if (item.layout && item.layout->contain(spring))
			return true;
	}
	return false;
}

bool BStackLayout::remove(BWidget* widget)
{
	if (!widget)
		return false;
	int index = member.items.find(widget);
	if (widget_member(widget)->upperLayout == this)
	{
		widget_member(widget)->upperLayout = 0;
		member.items.remove(widget);
		if (BWidget* host = layout_member(this)->host)
			host->removeChild(widget);
		if (member.current == index)
		{
			member.current = -1;
			setCurrentIndex(index);
		}
		BLayout::dirty();
		return true;
	}
	return false;
}
bool BStackLayout::remove(BLayout* layout)
{
	if (!layout || layout_member(layout)->parent != this)
		return false;
	int index = member.items.find(layout);
	if (layout_member(layout)->parent == this)
	{
		if (BWidget* host = this->host())
			widget_member(host)->takeLayout(layout);
		layout_member(layout)->parent = 0;
		member.items.remove(layout);
		if (member.current == index)
		{
			member.current = -1;
			setCurrentIndex(index);
		}
		BLayout::dirty();
		return true;
	}
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.layout && item.layout->remove(layout))
		{
			BLayout::dirty();
			return true;
		}
	}
	return false;
}
bool BStackLayout::remove(BSpacer* spacer)
{
	if (!spacer)
		return false;
	int index = member.items.find(spacer);
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.spacer == spacer)
		{
			spacer_member(spacer)->layout = 0;
			member.items.remove(i);
			if (member.current == index)
			{
				member.current = -1;
				setCurrentIndex(index);
			}
			BLayout::dirty();
			return false;
		}
	}
	return true;
}
bool BStackLayout::remove(BSpring* spring)
{
	if (!spring)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.spring == spring)
		{
			spring_member(spring)->layout = 0;
			member.items.remove(i);
			BLayout::dirty();
			return false;
		}
	}
	return true;
}
bool BStackLayout::clear()
{
	if (member.items.size())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			ILayoutItem& item = member.items[i];
			if (BWidget* widget = item.widget)
			{
				widget_member(widget)->upperLayout = 0;
				if (BWidget* host = layout_member(this)->host)
					host->removeChild(widget);
			}
			if (BLayout* layout = item.layout)
			{
				layout_member(layout)->setHost(0);
			}
		}
		member.items.clear();
		BLayout::dirty();
		return true;
	}
	return false;
}

BSizePolicy BStackLayout::sizePolicy() const
{
	BSizePolicy policy = layout_member(this)->sizePolicy;
	if (policy.widthPolicy() == Policy_None)
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			ILayoutItem& item = member.items[i];
			if (item.dirty)
				item.flush();
			if (item.ignored)
				continue;
			policy.widthPolicy() |= item.sizePolicy.widthPolicy();
		}
	}
	if (policy.heightPolicy() == Policy_None)
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			ILayoutItem& item = member.items[i];
			if (item.dirty)
				item.flush();
			if (item.ignored)
				continue;
			policy.heightPolicy() |= item.sizePolicy.heightPolicy();
		}
	}
	return policy;
}
BSize BStackLayout::baseSize() const
{
	BSize size;
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.dirty)
			item.flush();
		if (item.ignored)
			continue;
		if (item.spring)
			continue;
		size.height() += item.size.height();
		size.width() = bMax(size.width(), item.size.width());
	}
	return size;
}
BSize BStackLayout::sizeHint() const
{
	const BMargin& margin = this->margin();
	int maxWidth = margin.width();
	int maxHeight = margin.height();
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.dirty)
			item.flush();
		if (item.ignored)
			continue;
		BSize hsize;
		if (item.sizePolicy.widthPolicy() & Policy_Preferred)
			hsize.width() = item.sizeHint.width();
		if (item.sizePolicy.heightPolicy() & Policy_Preferred)
			hsize.height() = item.sizeHint.height();

		if (item.sizePolicy.widthPolicy() == Policy_Fixed)
			hsize.width() = item.fixedSize.width();
		if (item.sizePolicy.heightPolicy() == Policy_Fixed)
			hsize.height() = item.fixedSize.height();

		maxWidth = bMax(maxWidth, margin.width() + hsize.width());
		maxHeight = bMax(maxHeight, margin.height() + hsize.height());
	}
	BSize hsize(maxWidth, maxHeight);
	if (hsize.width() < 0)
		hsize.width() = 0;
	if (hsize.height() < 0)
		hsize.height() = 0;
	return hsize;
}
BSize BStackLayout::fixedSize() const
{
	BSize fixedSize = layout_member(this)->fixedSize;
	if (fixedSize.width() == 0 || fixedSize.height() == 0)
	{
		const BMargin& margin = this->margin();
		int fixedWidth = fixedSize.width();
		int fixedHeight = fixedSize.height();
		for (int i = 0; i < member.items.size(); i++)
		{
			ILayoutItem& item = member.items[i];
			if (item.dirty)
				item.flush();
			if (item.ignored)
				continue;
			fixedHeight += item.fixedSize.height();
			fixedWidth = bMax(fixedWidth, item.fixedSize.width());
		}
		if (fixedSize.width() == 0)
			fixedSize.width() = fixedWidth + margin.left() + margin.right();
		if (fixedSize.height() == 0)
			fixedSize.height() = fixedHeight + margin.top() + margin.bottom();
	}
	return fixedSize;
}
BSize BStackLayout::minSize() const
{
	const BMargin& margin = this->margin();
	int minWidth = margin.width();
	int minHeight = margin.height();
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.dirty)
			item.flush();
		if (item.ignored)
			continue;

		if (item.sizePolicy.heightPolicy() == Policy_Fixed)
			minHeight += item.fixedSize.height();
		else
			minHeight += item.minSize.height();

		if (item.sizePolicy.widthPolicy() == Policy_Fixed)
			minWidth = bMax(minWidth, margin.width() + item.fixedSize.width());
		else
			minWidth = bMax(minWidth, margin.width() + item.minSize.width());
	}
	BSize minSize(minWidth, minHeight);
	minSize.limit(layout_member(this)->minSize, layout_member(this)->maxSize);
	return minSize;
}
BSize BStackLayout::maxSize() const
{
	int maxWidth = layout_member(this)->maxSize.width();
	int maxHeight = layout_member(this)->maxSize.height();
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (item.dirty)
			item.flush();
		if (item.ignored)
			continue;

		if (item.sizePolicy.heightPolicy() == Policy_Fixed)
			maxHeight += item.fixedSize.height();
		else
			maxHeight += item.maxSize.height();

		if (item.sizePolicy.widthPolicy() == Policy_Fixed)
			maxWidth = bMax(maxWidth, item.fixedSize.width());
		else
			maxWidth = bMax(maxWidth, item.maxSize.width());
	}
	return BSize(maxWidth, maxHeight);
}
bool BStackLayout::adjust()
{
	if (!layout_member(this)->dirty)
		return false;

	if (this->rect().empty())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			member.items[i].adjust(0, 0, 0, 0);
		}
		return true;
	}

	const BRect& rect = this->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	const BMargin& margin = this->margin();
	int posX = x + margin.left();
	int posY = y + margin.top();
	int width = rect.width() - margin.width();
	int height = rect.height() - margin.height();

	if (member.items.check(member.current))
	{
		ILayoutItem& item = member.items(member.current);
		item.prepare(height, width, height);
		item.adjust(posX, posY, width, height);
	}

	layout_member(this)->dirty = false;
	if (this->checks(Signal_Adjust))
		this->emit(Signal_Adjust, layout_member(this)->rect);
	return true;
}

void BStackLayout::attached(BWidget* host)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (BWidget* widget = item.widget)
			host->addChild(widget);
		if (BLayout* layout = item.layout)
			layout_member(layout)->setHost(host);
	}
}
void BStackLayout::detached(BWidget* host)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		ILayoutItem& item = member.items[i];
		if (BWidget* widget = item.widget)
			host->removeChild(widget);
		if (BLayout* layout = item.layout)
			layout_member(layout)->setHost(0);
	}
}
